package com.citybox.shared.utils.http;

import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

public class HttpUtils {

    private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    // 默认超时时间（毫秒）
    private static final int DEFAULT_CONNECT_TIMEOUT = 3000;
    private static final int DEFAULT_READ_TIMEOUT = 3000;

    /**
     * 发送POST请求（带请求头）
     */
    public static String doPost(String pathUrl, String data, Map<String, String> header) throws IOException {
        return doPostInternal(pathUrl, data, header);
    }
    /**
     * 发送POST请求（不带请求头）
     */
    public static String doPost(String pathUrl, String data) throws IOException {
        return doPostInternal(pathUrl, data, null);
    }
    /**
     * 内部实现方法
     */
    private static String doPostInternal(String pathUrl, String data, Map<String, String> header) throws IOException {
        // 参数校验
        validateParameters(pathUrl, data);

        HttpURLConnection conn = null;
        try {
            conn = createConnection(pathUrl);
            configureConnection(conn);

            // 添加自定义请求头
            if (header != null) {
                addHeaders(conn, header);
            }

            // 发送请求体
            sendRequestBody(conn, data);

            // 读取并返回响应
            return readResponse(conn);

        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
    /**
     * 参数校验
     */
    private static void validateParameters(String pathUrl, String data) {
        if (pathUrl == null || pathUrl.trim().isEmpty()) {
            throw new IllegalArgumentException("URL cannot be null or empty");
        }
        if (data == null) {
            throw new IllegalArgumentException("Request data cannot be null");
        }
    }
    /**
     * 创建HTTP连接
     */
    private static HttpURLConnection createConnection(String pathUrl) throws IOException {
        URL url = new URL(pathUrl);
        return (HttpURLConnection) url.openConnection();
    }
    /**
     * 配置连接参数
     */
    private static void configureConnection(HttpURLConnection conn) throws IOException {
        conn.setRequestMethod("POST");
        conn.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
        conn.setReadTimeout(DEFAULT_READ_TIMEOUT);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
    }
    /**
     * 添加请求头
     */
    private static void addHeaders(HttpURLConnection conn, Map<String, String> header) {
        // 参数校验
        if (conn == null) {
            throw new IllegalArgumentException("HttpURLConnection cannot be null");
        }

        // 空headers处理
        if (header == null || header.isEmpty()) {
            return;
        }

        header.forEach((key, value) -> {
            // 过滤空key
            if (key != null && !key.trim().isEmpty()) {
                conn.setRequestProperty(key.trim(), value != null ? value.trim() : "");
            }
        });
    }
    /**
     * 发送请求体
     */
    private static void sendRequestBody(HttpURLConnection conn, String data) throws IOException {
        try (OutputStream os = conn.getOutputStream();
             OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
            writer.write(data);
            writer.flush();
        }
    }
    /**
     * 安全读取HTTP响应（统一实现）
     */
    private static String readResponse(HttpURLConnection conn) throws IOException {
        int status = conn.getResponseCode();
        InputStream inputStream = null;

        try {
            inputStream = status < 400 ? conn.getInputStream() : conn.getErrorStream();

            if (inputStream == null) {
                throw new IOException("HTTP " + status + ": No response body");
            }

            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            StringBuilder response = new StringBuilder();
            // 8KB缓冲区
            char[] buffer = new char[8192];
            int charsRead;

            while ((charsRead = reader.read(buffer)) != -1) {
                response.append(buffer, 0, charsRead);
            }

            if (status >= 400) {
                throw new IOException("HTTP " + status + ": " + response.toString());
            }
            return response.toString();
        } finally {
            // 确保输入流被关闭
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // 记录日志但不要抛出，以免掩盖原始异常
                    System.err.println("Warning: Failed to close input stream: " + e.getMessage());
                }
            }
        }
    }


    public static String doGet(String url, Map<String, String> paramMap, Map<String, String> headers) throws IOException {
        validateUrl(url);

        HttpURLConnection conn = null;
        try {
            //拼接参数
            url = addParams(url,paramMap);
            conn = createConnection(url);
            configureGetConnection(conn);
            //head头
            addHeaders(conn, headers);
            return readResponse(conn);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
    public static String doGet(String url) throws IOException {
        validateUrl(url);

        try (CloseableHttpClient httpClient = createHttpClient(url);
             CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(url))) {

            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new IOException("HTTP request failed with status: " + statusCode);
            }

            HttpEntity httpEntity = httpResponse.getEntity();
            String result = EntityUtils.toString(httpEntity);
            return result.replaceAll("\r", "");
        }
    }

    private static void validateUrl(String url) {
        if (url == null || url.trim().isEmpty()) {
            throw new IllegalArgumentException("URL cannot be null or empty");
        }
    }
    private static void configureGetConnection(HttpURLConnection conn) throws IOException {
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
        conn.setReadTimeout(DEFAULT_READ_TIMEOUT);
        // GET请求不需要输出
        conn.setDoOutput(false);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("connection", "Keep-Alive");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
    }

    private static String addParams(String url, Map<String, String> paramMap) {
        if (paramMap == null || paramMap.isEmpty()) {
            // 直接返回原始URL
            return url;
        }
        //开始拼接
        StringBuilder urlWithParams = new StringBuilder(url).append("?");
        for (Map.Entry<String, String> entry : paramMap.entrySet()) {
            urlWithParams.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
                    .append("=")
                    .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
                    .append("&");
        }
        // 删除最后一个&
        return urlWithParams.substring(0, urlWithParams.length() - 1);
    }

    private static CloseableHttpClient createHttpClient(String url) {
        return url.startsWith("https://")
                ? createSSLClientDefault()
                : createDefaultHttpClient();
    }

    private static CloseableHttpClient createDefaultHttpClient() {
        return HttpClients.custom()
                .setDefaultRequestConfig(getDefaultRequestConfig())
                .build();
    }
    private static RequestConfig getDefaultRequestConfig() {
        return RequestConfig.custom()
                .setSocketTimeout(DEFAULT_READ_TIMEOUT)
                .setConnectTimeout(DEFAULT_CONNECT_TIMEOUT)
                .setConnectionRequestTimeout(DEFAULT_CONNECT_TIMEOUT)
                .build();
    }

    /**
     * 信任所有https证书请求
     */
    private static CloseableHttpClient createSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
            return HttpClients.custom().setSSLSocketFactory(sslsf).setDefaultRequestConfig(getConfig(null)).build();
        } catch (KeyManagementException e) {
            logger.error("创建https访问异常【{}】", e);
        } catch (NoSuchAlgorithmException e) {
            logger.error("创建https访问异常【{}】", e);
        } catch (KeyStoreException e) {
            logger.error("创建https访问异常【{}】", e);
        }
        return HttpClients.custom().setDefaultRequestConfig(getConfig(null)).build();
    }

    /**
     * 创建CloseableHttpClient
     */
    /*private static CloseableHttpClient createSSLClientDefaultBycrert(String certKey, String certPath) {
        // 判断是否传入证书和密钥
        if (certPath != null && certKey != null) {
            logger.info("创建CloseableHttpClient使用证书，证书路径【{}】，证书密码【{}】", certPath, certKey);
            try (FileInputStream instream = new FileInputStream(new File(certPath))) {
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                keyStore.load(instream, certKey.toCharArray());

                SSLContext sslContext = SSLContexts.custom()
                        .loadKeyMaterial(keyStore, certKey.toCharArray())
                        .loadTrustMaterial(null, new TrustSelfSignedStrategy())
                        .build();

                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslContext,
                        new String[]{"TLSv1.2", "TLSv1.3"}, // 更新为更安全的协议版本
                        null,
                        SSLConnectionSocketFactory.getDefaultHostnameVerifier() // 或者使用更严格的验证器
                );

                return HttpClients.custom()
                        .setDefaultRequestConfig(getConfig(null))
                        .setSSLSocketFactory(sslsf)
                        .build();
            } catch (Exception e) {
                logger.error("创建CloseableHttpClient使用证书，证书路径【{}】，证书密码【{}隐藏】，发生异常【{}】", certPath, "******", e.getMessage(), e);
                throw new RuntimeException("无法创建SSL配置的HttpClient", e); // 抛出异常让调用者处理
            }
        } else {
            logger.info("创建CloseableHttpClient未使用证书");
            return createSSLClientDefault();
        }
    }*/


    private static RequestConfig getConfig(Integer timeout) {
        RequestConfig config;
        if (timeout != null && timeout != 0) {
            config = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(10000)
                    .setConnectionRequestTimeout(10000).build();
        } else {
            config = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000)
                    .setConnectionRequestTimeout(10000).build();
        }
        return config;
    }

}
